package handler

import (
    "archive/zip"
    "encoding/base64"
    "encoding/json"
    "encoding/pem"
    "errors"
    "fcs-api/src/app/business"
    "fcs-api/src/app/model"
    "fcs-api/src/app/service/ksvc"
    "fcs-api/src/app/service/namespace"
    "fcs-api/src/app/utils"
    "fmt"
    "io"
    "io/ioutil"
    "net/http"
    "os"
    "os/exec"
    "path/filepath"
    "reflect"
    "strconv"
    "strings"
    "time"
    "unicode/utf8"

    commonError "git.inspur.com/x/common/error"
    "git.inspur.com/x/common/i18n"
    "git.inspur.com/x/common/validator"
    "git.inspur.com/x/iam-adapter-go/resource"
    "github.com/emicklei/go-restful"
    "github.com/google/uuid"
    "k8s.io/apimachinery/pkg/util/rand"
)

var cmd *exec.Cmd

const (
    defaultFunctionTimeout = 10
    defaultFunctionMemory  = 128
)

/**
验证添加函数参数
返回值：是否验证通过，失败原因
*/
func validateAddFunction(request *restful.Request) (*model.FuncInfo, error) {
    groupId := request.PathParameter("groupId")

    functionInfo := new(model.FuncInfo)
    if err := ReadAndValidateEntity(request, functionInfo); err != nil {
        return nil, err
    }

    user := currentUser(request)
    if functionInfo.GroupId == model.ExtIds {
        functionInfo.GroupName = model.ExtIds
        functionInfo.RegionCode = strings.Split(strings.Replace(model.Base.CallbackEndpoint, "http://", "", -1), ".")[0]
        if functionInfo.RegionCode == "cn-north-3" {
            functionInfo.RegionName = "华北三"
        }
    } else {
        group, err := model.GetGroupByUserAndId(user, groupId)
        if err != nil {
            return nil, model.InvalidParameterError(model.FunctionCreateError, i18n.GetMessage(request.Request, "fcs.group.query_info"))
        }
        functionInfo.GroupName = group.Name
    }

    functionInfo.Id = uuid.New().String()
    functionInfo.Creator = user.Id
    functionInfo.CreatorName = user.Name
    functionInfo.A5tId = user.AccountId
    functionInfo.A5tName = user.AccountName
    functionInfo.GroupId = groupId
    functionInfo.VersionName = model.FunctionLatestVersion

    if functionInfo.Runtime == model.CustomContainer {
        if functionInfo.ContainerPort == "" {
            functionInfo.ContainerPort = model.DefaultUserContainerPort
        }
    }

    // 如果有层的配置，那么把配置转换成路径，添加到结构体中
    if len(functionInfo.BindingLayers) != 0 {
        if len(functionInfo.BindingLayers) > 5 {
            return nil, model.InvalidParameterError(model.FunctionCreateError, "函数最多只能绑定5个层")
        }
        layerPath, err := validateLayerIds(functionInfo.BindingLayers, functionInfo.Runtime, user)
        if err != nil {
            return nil, err
        }
        functionInfo.LayerPath = layerPath
    }

    return functionInfo, nil
}

func validateCopyFunction(request *restful.Request) (*model.FuncInfo, *model.FuncCopyInfo, error) {
    groupId := request.PathParameter("groupId")
    funcId := request.PathParameter("functionId")
    functionCopyInfo := new(model.FuncCopyInfo)
    if err := ReadAndValidateEntity(request, functionCopyInfo); err != nil {
        return nil, nil, err
    }

    user := currentUser(request)
    groupInfo, err := model.GetGroupByUserAndId(user, functionCopyInfo.TargetGroupId)
    if err != nil || groupInfo == nil {
        return nil, nil, model.InvalidParameterError(model.FunctionUpdateError, i18n.GetMessage(request.Request, "fcs.group.query_info"))
    }
    oldFuncInfo, _, err := findFuncInfoFromDB(groupId, funcId, user)
    if err != nil {
        return nil, nil, model.InvalidParameterError(model.FunctionUpdateError, i18n.GetMessage(request.Request, "fcs.function.detail"))
    }
    if oldFuncInfo.Status == model.FunctionUpdating || oldFuncInfo.Status == model.FunctionCreating {
        return nil, nil, model.InvalidParameterError(model.FunctionUpdateError, i18n.GetMessage(request.Request, "fcs.function.updating"))
    }
    functionCopyInfo.SourceFunctionName = oldFuncInfo.FunctionName
    functionCopyInfo.SourceGroupName = oldFuncInfo.GroupName
    functionCopyInfo.TragetGroupName = groupInfo.Name
    if functionCopyInfo.IsCopyConfig {
        oldFuncInfo.Id = uuid.New().String()
        oldFuncInfo.FunctionName = functionCopyInfo.NewFunctionName
        oldFuncInfo.GroupId = groupInfo.Id
        oldFuncInfo.GroupName = groupInfo.Name
        oldFuncInfo.Description = functionCopyInfo.Description
        return oldFuncInfo, functionCopyInfo, nil
    }
    newFunctionInfo := new(model.FuncInfo)
    newFunctionInfo.Id = uuid.New().String()
    newFunctionInfo.FunctionName = functionCopyInfo.NewFunctionName
    newFunctionInfo.GroupId = groupInfo.Id
    newFunctionInfo.GroupName = groupInfo.Name
    newFunctionInfo.Description = functionCopyInfo.Description
    newFunctionInfo.Creator = user.Id
    newFunctionInfo.CreatorName = user.Name
    newFunctionInfo.A5tId = user.AccountId
    newFunctionInfo.A5tName = user.AccountName
    newFunctionInfo.VersionName = model.FunctionLatestVersion
    newFunctionInfo.Runtime = oldFuncInfo.Runtime
    newFunctionInfo.Handler = oldFuncInfo.Handler
    newFunctionInfo.Timeout = defaultFunctionTimeout
    newFunctionInfo.MemorySize = defaultFunctionMemory
    return newFunctionInfo, functionCopyInfo, nil
}

func validateLayerIds(ids []string, runtime string, user *resource.User) ([]string, error) {
    var layerPaths []string
    for _, id := range ids {
        if layer, err := model.GetLayerByUserAndLayerId(user, id); err != nil {
            return nil, err
        } else {
            isMatch := false
            for _, layerRuntime := range layer.Runtimes {
                if runtime == layerRuntime {
                    layerPaths = append(layerPaths, layer.GetLayerPath())
                    isMatch = true
                    break
                }
            }
            if !isMatch {
                return nil, model.InvalidParameterError(model.FunctionUpdateError, "层和函数runtime不匹配")
            }
        }
    }
    return layerPaths, nil
}

func validateUpdateFunction(request *restful.Request) (*model.UpdateModel, error) {

    groupId := request.PathParameter("groupId")
    funcId := request.PathParameter("functionId")
    user := currentUser(request)

    newFuncInfo := new(model.FuncInfo)
    if err := ReadAndValidateEntity(request, newFuncInfo); err != nil {
        return nil, err
    }

    if newFuncInfo.Runtime == model.CustomContainer {
        if newFuncInfo.ContainerPort == "" {
            newFuncInfo.ContainerPort = model.DefaultUserContainerPort
        }
    }
    oldFuncInfo, latestVersionInfo, err := findFuncInfoFromDB(groupId, funcId, user)
    if err != nil {
        return nil, model.InvalidParameterError(model.FunctionUpdateError, i18n.GetMessage(request.Request, "fcs.function.detail"))
    }
    if oldFuncInfo.Status == model.FunctionUpdating || oldFuncInfo.Status == model.FunctionCreating {
        return nil, model.InvalidParameterError(model.FunctionUpdateError, i18n.GetMessage(request.Request, "fcs.function.updating"))
    }

    err = utils.SimpleCopyProperties(oldFuncInfo, latestVersionInfo)
    if err != nil {
        return nil, model.InvalidParameterError(model.FunctionUpdateError, i18n.GetMessage(request.Request, "fcs.function.updating"))
    }

    var vpcUpdateStatus model.VpcUpdateStatus
    var oldVpcInfo *model.VpcInfo // 保留修改之前之前vpc的信息
    // 判断vpc信息如何进行更改
    if oldFuncInfo.EnableVpc == 0 && newFuncInfo.EnableVpc == 1 {
        vpcUpdateStatus = model.Vpc_Need_Add
    } else if oldFuncInfo.EnableVpc == 1 && newFuncInfo.EnableVpc == 0 {
        vpcUpdateStatus = model.Vpc_Need_Delete
        oldVpcInfo = &model.VpcInfo{
            VpcId:    oldFuncInfo.VpcInfo.VpcId,
            SubnetId: oldFuncInfo.VpcInfo.SubnetId,
        }
    } else if oldFuncInfo.EnableVpc == 1 && newFuncInfo.EnableVpc == 1 {
        if oldFuncInfo.VpcInfo.VpcId == newFuncInfo.VpcInfo.VpcId {
            vpcUpdateStatus = model.Vpc_No_Need_Update
        } else {
            vpcUpdateStatus = model.Vpc_Need_Modify
            oldVpcInfo = &model.VpcInfo{
                VpcId:    oldFuncInfo.VpcInfo.VpcId,
                SubnetId: oldFuncInfo.VpcInfo.SubnetId,
            }
        }
    } else {
        vpcUpdateStatus = model.Vpc_No_Need_Update
    }

    // 将数据库中的数据赋值成更新请求中的值，同时传给前端
    err = utils.SimpleCopyProperties(oldFuncInfo, newFuncInfo)
    // 特殊处理可能为空的字段
    oldFuncInfo.Envs = newFuncInfo.Envs
    oldFuncInfo.EnablePublic = newFuncInfo.EnablePublic
    oldFuncInfo.EnableVpc = newFuncInfo.EnableVpc
    oldFuncInfo.Description = newFuncInfo.Description
    oldFuncInfo.InitHandler = newFuncInfo.InitHandler
    oldFuncInfo.InitTimeout = newFuncInfo.InitTimeout
    oldFuncInfo.ReserveNumber = newFuncInfo.ReserveNumber
    if err != nil {
        return nil, model.InvalidParameterError(model.FunctionUpdateError, i18n.GetMessage(request.Request, "fcs.function.updating"))
    }

    if len(oldFuncInfo.BindingLayers) != 0 {
        if len(oldFuncInfo.BindingLayers) > 5 {
            return nil, model.InvalidParameterError(model.FunctionCreateError, "函数最多只能绑定5个层")
        }
        layerPath, err := validateLayerIds(oldFuncInfo.BindingLayers, oldFuncInfo.Runtime, user)
        if err != nil {
            return nil, err
        }
        oldFuncInfo.LayerPath = layerPath
    }

    // 修改最新版本的值
    err = utils.SimpleCopyProperties(latestVersionInfo, newFuncInfo)
    // 特殊处理可能为空的字段
    latestVersionInfo.Envs = newFuncInfo.Envs
    latestVersionInfo.EnablePublic = newFuncInfo.EnablePublic
    latestVersionInfo.EnableVpc = newFuncInfo.EnableVpc
    latestVersionInfo.Description = newFuncInfo.Description
    latestVersionInfo.InitHandler = newFuncInfo.InitHandler
    latestVersionInfo.InitTimeout = newFuncInfo.InitTimeout
    latestVersionInfo.ReserveNumber = newFuncInfo.ReserveNumber
    if err != nil {
        return nil, model.InvalidParameterError(model.FunctionUpdateError, i18n.GetMessage(request.Request, "fcs.function.updating"))
    }

    return &model.UpdateModel{
        FuncInfo:        oldFuncInfo,
        VersionInfo:     latestVersionInfo,
        VpcUpdateStatus: vpcUpdateStatus,
        OldVcpInfo:      oldVpcInfo,
    }, nil
}

func validateUpdateFunctionLayer(request *restful.Request) (*model.UpdateModel, error) {

    groupId := request.PathParameter("groupId")
    funcId := request.PathParameter("functionId")
    user := currentUser(request)

    layerInfo := new(model.LayerInfo)
    if err := ReadAndValidateEntity(request, layerInfo); err != nil {
        return nil, err
    }

    oldFuncInfo, latestVersionInfo, err := findFuncInfoFromDB(groupId, funcId, user)
    if err != nil {
        return nil, err
    }
    if oldFuncInfo.Status == model.FunctionUpdating || oldFuncInfo.Status == model.FunctionCreating {
        return nil, model.InvalidParameterError(model.FunctionUpdateError, i18n.GetMessage(request.Request, "fcs.function.updating"))
    }

    if reflect.DeepEqual(oldFuncInfo.BindingLayers, layerInfo.BindingLayers) {
        return &model.UpdateModel{FuncInfo: oldFuncInfo}, model.NewBusinessError(errors.New("don't need update"), model.NoNeedUpdateResource)
    }

    // 修改最新版本的值
    latestVersionInfo.BindingLayers = layerInfo.BindingLayers
    oldFuncInfo.BindingLayers = layerInfo.BindingLayers

    // 将数据库中的数据赋值成更新请求中的值，同时传给前端
    if len(oldFuncInfo.BindingLayers) != 0 {
        layerPath, err := validateLayerIds(oldFuncInfo.BindingLayers, oldFuncInfo.Runtime, user)
        if err != nil {
            return nil, err
        }
        oldFuncInfo.LayerPath = layerPath
    }

    return &model.UpdateModel{
        FuncInfo:    oldFuncInfo,
        VersionInfo: latestVersionInfo,
    }, nil
}

func validateGetFunctionList(request *restful.Request) (*model.FunctionListQuery, error) {
    // groupId 用于查询指定应用下的函数，如果为空则查询所有应用的函数
    groupId := request.PathParameter("groupId")
    pageNoStr := request.QueryParameter("pageNo")
    pageSizeStr := request.QueryParameter("pageSize")
    // （可选）通过函数名称查询
    functionName := request.QueryParameter("name")

    functionListQuery := &model.FunctionListQuery{
        PageNo:       1,
        PageSize:     1000000,
        Creator:      "",
        AccountId:    "",
        FunctionName: "",
        GroupId:      "",
    }
    if pageNoStr != "" || pageSizeStr != "" {
        pageNo, err := strconv.Atoi(pageNoStr)
        if err != nil {
            return nil, commonError.BadRequestError(model.FunctionRequestValidError, i18n.GetMessage(request.Request, "fcs.function.list_param"))
        }
        pageSize, err := strconv.Atoi(pageSizeStr)
        if err != nil {
            return nil, commonError.BadRequestError(model.FunctionRequestValidError, i18n.GetMessage(request.Request, "fcs.function.list_param"))
        }
        functionListQuery.PageNo = pageNo
        functionListQuery.PageSize = pageSize
    }
    user := currentUser(request)
    if groupId != "" {
        _, err := model.GetGroupByUserAndId(user, groupId)
        if err != nil {
            return nil, model.InvalidParameterError(model.FunctionRequestValidError, i18n.GetMessage(request.Request, "fcs.group.query_info"))
        }
    }
    functionListQuery.FunctionName = functionName
    functionListQuery.GroupId = groupId
    functionListQuery.Creator = user.Id
    functionListQuery.AccountId = user.AccountId
    return functionListQuery, nil
}

func validateIsFunctionExist(request *restful.Request) (bool, error) {
    groupId := request.PathParameter("groupId")
    funcName := request.QueryParameter("functionName")
    user := currentUser(request)

    return model.CheckFuncExist(groupId, funcName, user.Id)
}

func validateGetFunctionDetail(request *restful.Request) (*model.FuncInfo, error) {
    groupId := request.PathParameter("groupId")
    funcId := request.PathParameter("functionId")
    versionName := request.PathParameter("versionName")
    user := currentUser(request)

    funcInfo, err := model.GetFuncInfoByGroup(groupId, funcId, user)
    if err != nil {
        return nil, model.InvalidParameterError(model.FunctionGetError, i18n.GetMessage(request.Request, "fcs.function.detail"))
    }
    versionInfo, err := model.GetVersion(funcId, versionName)
    if err != nil {
        return nil, model.InvalidParameterError(model.FunctionGetError, i18n.GetMessage(request.Request, "fcs.version.version_info"))
    }
    if versionInfo.FuncId != funcId {
        return nil, model.InvalidParameterError(model.FunctionGetError, i18n.GetMessage(request.Request, "fcs.version.version_info"))
    }
    groupInfo, err := model.GetGroupById(groupId)
    if err != nil {
        return nil, model.InvalidParameterError(model.FunctionGetError, i18n.GetMessage(request.Request, "fcs.function.version"))
    }

    funcInfo.Description = versionInfo.Description
    funcInfo.Timeout = versionInfo.Timeout
    funcInfo.Envs = versionInfo.Envs
    funcInfo.MemorySize = versionInfo.MemorySize
    funcInfo.Handler = versionInfo.Handler
    funcInfo.VersionId = versionInfo.VersionId
    funcInfo.VersionName = versionInfo.VersionName
    funcInfo.Concurrency = versionInfo.Concurrency
    funcInfo.ReserveNumber = versionInfo.ReserveNumber
    funcInfo.EnablePublic = versionInfo.EnablePublic
    funcInfo.VpcInfo = versionInfo.VpcInfo
    funcInfo.EnableVpc = versionInfo.EnableVpc
    funcInfo.GroupDisplayName = groupInfo.DisplayName
    funcInfo.ContainerImage = versionInfo.ContainerImage
    funcInfo.ContainerCommand = versionInfo.ContainerCommand
    funcInfo.ContainerArgs = versionInfo.ContainerArgs
    funcInfo.ContainerPort = versionInfo.ContainerPort
    funcInfo.InitHandler = versionInfo.InitHandler
    funcInfo.InitTimeout = versionInfo.InitTimeout
    funcInfo.BindingLayers = versionInfo.BindingLayers
    if len(funcInfo.BindingLayers) != 0 {
        layerInfos, _ := model.GetLayerByVersionIds(user.Id, funcInfo.BindingLayers)
        for i, info := range layerInfos {
            info.Priority = i + 1
        }
        funcInfo.LayerInfos = layerInfos
    } else {
        funcInfo.LayerInfos = make([]*model.Layer, 0)
    }
    return funcInfo, nil
}

func validateCreateLayer(request *restful.Request) (*model.Layer, error) {
    layer := new(model.Layer)
    if err := ReadAndValidateEntity(request, layer); err != nil {
        return nil, err
    }

    nameLength := utf8.RuneCountInString(layer.Name)
    if nameLength < 2 {
        return nil, model.InvalidParameterError(model.LayerGetError, "层名称长度不能小于2个字符")
    }
    if nameLength > 60 {
        return nil, model.InvalidParameterError(model.LayerGetError, "层名称长度不能超过60个字符")
    }
    if utf8.RuneCountInString(layer.Description) > 500 {
        return nil, model.InvalidParameterError(model.LayerGetError, "层描述长度不能超过500个字符")
    }
    if len(layer.Runtimes) > 5 {
        return nil, model.InvalidParameterError(model.LayerGetError, "运行时数量不能超过5个")
    }
    if len(layer.Runtimes) == 0 {
        return nil, model.InvalidParameterError(model.LayerGetError, "运行时不能为空")
    }
    user := currentUser(request)
    if exist, err := model.CheckLayerExist(user.Id, layer.Name); err != nil {
        return nil, model.InvalidParameterError(model.LayerGetError, "查询层信息失败")
    } else {
        if !exist {
            layer.Id = uuid.New().String()
            layer.Creator = user.Id
            layer.CreatorName = user.Name
            layer.A5tId = user.AccountId
            layer.A5tName = user.AccountName
            layer.Version = "1"
            layer.RootId = uuid.New().String()
            return layer, nil
        } else {
            return nil, model.InvalidParameterError(model.LayerGetError, "层已经存在")
        }
    }
}

func validateCreateLayerVersion(request *restful.Request) (*model.Layer, error) {
    layerId := request.PathParameter("layerId")
    layer := new(model.Layer)
    if err := ReadAndValidateEntity(request, layer); err != nil {
        return nil, err
    }
    if utf8.RuneCountInString(layer.Description) > 500 {
        return nil, model.InvalidParameterError(model.LayerGetError, "层描述长度不能超过500个字符")
    }
    if len(layer.Runtimes) > 5 {
        return nil, model.InvalidParameterError(model.LayerGetError, "运行时数量不能超过5个")
    }
    if len(layer.Runtimes) == 0 {
        return nil, model.InvalidParameterError(model.LayerGetError, "运行时不能为空")
    }
    user := currentUser(request)
    if rootLayer, err := model.GetLayerByUserAndId(user, layerId); err != nil {
        _ = logger.Error("validateCreateLayerVersion: get layer info error, layerId: %s, err: %s", layerId, err)
        return nil, model.InvalidParameterError(model.LayerGetError, "查询层信息失败")
    } else {
        count, err := model.GetLayerCountAll(layerId)
        if err != nil {
            _ = logger.Error("validateCreateLayerVersion: get layer count error, layerId: %s, err: %s", layerId, err)
            return nil, model.InvalidParameterError(model.LayerGetError, "查询层信息失败")
        }
        layer.Id = uuid.New().String()
        layer.Version = strconv.FormatInt(count+1, 10)
        layer.Creator = rootLayer.Creator
        layer.CreatorName = rootLayer.CreatorName
        layer.A5tId = rootLayer.A5tId
        layer.A5tName = rootLayer.A5tName
        layer.RootId = rootLayer.RootId
        layer.Name = rootLayer.Name
        layer.RegionName = rootLayer.RegionName
        layer.RegionCode = rootLayer.RegionCode
        return layer, nil
    }
}

func validateGetLayerList(request *restful.Request) (*model.LayerListQuery, error) {

    pageNoStr := request.QueryParameter("pageNo")
    pageSizeStr := request.QueryParameter("pageSize")
    // （可选）通过层名称查询
    layerName := request.QueryParameter("name")
    if layerName != "" && utils.IsSQLInject(layerName) {
        return nil, model.InvalidParameterError(model.FunctionLogError, i18n.GetMessage(request.Request, "fcs.param_error"))
    }
    layerListQuery := &model.LayerListQuery{
        PageNo:    1,
        PageSize:  1000000,
        Creator:   "",
        AccountId: "",
        LayerName: "",
    }
    if pageNoStr != "" || pageSizeStr != "" {
        pageNo, err := strconv.Atoi(pageNoStr)
        if err != nil {
            return nil, commonError.BadRequestError(model.FunctionRequestValidError, "pageNO不是数字格式")
        }
        pageSize, err := strconv.Atoi(pageSizeStr)
        if err != nil {
            return nil, commonError.BadRequestError(model.FunctionRequestValidError, "pageSize不是数字格式")
        }
        layerListQuery.PageNo = pageNo
        layerListQuery.PageSize = pageSize
    }
    user := currentUser(request)
    layerListQuery.LayerName = layerName
    layerListQuery.Creator = user.Id
    layerListQuery.AccountId = user.AccountId
    return layerListQuery, nil
}

func validateGetLayerVersionList(request *restful.Request) (string, error) {

    rootId := request.PathParameter("layerId")
    user := currentUser(request)
    if rootLayer, err := model.GetLayerByUserAndId(user, rootId); err != nil {
        _ = logger.Error("validateCreateLayerVersion: get layer info error, rootId: %s, err: %s", rootId, err)
        return "", model.InvalidParameterError(model.LayerGetError, "查询层信息失败")
    } else {
        return rootLayer.RootId, nil
    }
}

func validateGetLayerRuntimeList(request *restful.Request) (string, error) {

    runtime := request.QueryParameter("runtime")
    if !model.RuntimeSet.Has(runtime) {
        return "", model.InvalidParameterError(model.LayerGetError, "runtime参数不正确")
    }
    return runtime, nil
}

func validateDeleteLayerVersion(request *restful.Request) (string, string, error) {

    rootId := request.PathParameter("layerId")
    version := request.PathParameter("versionName")
    user := currentUser(request)
    if rootLayer, err := model.GetLayerVersionByUserAndId(user, rootId, version); err != nil {
        _ = logger.Error("validateCreateLayerVersion: get layer info error, layerId: %s, err: %s", rootId, err)
        return "", "", model.InvalidParameterError(model.LayerGetError, "查询层信息失败")
    } else {
        used, err := model.CheckLayerIsUsed(rootLayer.Id)
        if err != nil {
            _ = logger.Error("validateCreateLayerVersion: get layer info error, layerId: %s, err: %s", rootId, err)
            return "", "", model.InvalidParameterError(model.LayerGetError, "查询层信息失败")
        }
        if used {
            return "", "", model.InvalidParameterError(model.LayerGetError, "层有绑定的函数，需要先解除绑定")
        }
        return rootLayer.RootId, version, nil
    }
}

func validateCreateVersion(request *restful.Request) (*model.FuncInfo, *model.FuncVersion, error) {
    groupId := request.PathParameter("groupId")
    funcId := request.PathParameter("functionId")
    version := new(model.Version)
    if err := ReadAndValidateEntity(request, version); err != nil {
        return nil, nil, err
    }
    versionName := model.FunctionLatestVersion
    user := currentUser(request)

    funcInfo, err := model.GetFuncInfoByGroup(groupId, funcId, user)
    if err != nil {
        return nil, nil, model.InvalidParameterError(model.FunctionGetError, i18n.GetMessage(request.Request, "fcs.function.detail"))
    }
    versionInfo, err := model.GetVersion(funcId, versionName)
    if err != nil {
        return nil, nil, model.InvalidParameterError(model.FunctionGetError, i18n.GetMessage(request.Request, "fcs.version.version_info"))
    }
    if versionInfo.FuncId != funcId {
        return nil, nil, model.InvalidParameterError(model.FunctionGetError, i18n.GetMessage(request.Request, "Version didn't match"))
    }
    newVersion := funcInfo.MaxVersion + 1

    // 修改version信息，在后面business中可以直接将这条数据插入
    versionInfo.VersionId = ""
    versionInfo.VersionName = strconv.Itoa(newVersion)
    // 清除版本的预留实例，暂时不做
    versionInfo.ReserveNumber = 0
    versionInfo.Description = version.Description

    funcInfo.Description = versionInfo.Description
    funcInfo.Timeout = versionInfo.Timeout
    funcInfo.Envs = versionInfo.Envs
    funcInfo.MemorySize = versionInfo.MemorySize
    funcInfo.GpuNum = versionInfo.GpuNum
    funcInfo.GpuType = versionInfo.GpuType
    funcInfo.Handler = versionInfo.Handler
    funcInfo.VersionId = versionInfo.VersionId
    funcInfo.VersionName = versionInfo.VersionName
    funcInfo.Concurrency = versionInfo.Concurrency
    funcInfo.ReserveNumber = versionInfo.ReserveNumber
    funcInfo.EnablePublic = versionInfo.EnablePublic
    funcInfo.MaxVersion = newVersion
    funcInfo.ContainerImage = versionInfo.ContainerImage
    funcInfo.ContainerCommand = versionInfo.ContainerCommand
    funcInfo.ContainerArgs = versionInfo.ContainerArgs
    funcInfo.ContainerPort = versionInfo.ContainerPort
    funcInfo.InitHandler = versionInfo.InitHandler
    funcInfo.InitTimeout = versionInfo.InitTimeout
    funcInfo.BindingLayers = versionInfo.BindingLayers
    if len(funcInfo.BindingLayers) != 0 {
        layerPath, err := validateLayerIds(funcInfo.BindingLayers, funcInfo.Runtime, user)
        if err != nil {
            return nil, nil, err
        }
        funcInfo.LayerPath = layerPath
    }

    return funcInfo, versionInfo, nil
}

func validateFuncInfo(request *restful.Request) (*model.FuncInfo, error) {
    groupId := request.PathParameter("groupId")
    funcId := request.PathParameter("functionId")
    user := currentUser(request)
    funcInfo, err := model.GetFuncInfoByGroup(groupId, funcId, user)
    if err != nil {
        return nil, model.InvalidParameterError(model.FunctionDeleteError, i18n.GetMessage(request.Request, "fcs.function.detail"))
    }
    return funcInfo, nil
}

func validateDeleteFunction(request *restful.Request) (*model.FuncInfo, error) {
    groupId := request.PathParameter("groupId")
    funcId := request.PathParameter("functionId")
    user := currentUser(request)
    funcInfo, err := model.GetFuncInfoByGroup(groupId, funcId, user)
    if err != nil {
        return nil, model.InvalidParameterError(model.FunctionDeleteError, i18n.GetMessage(request.Request, "fcs.function.detail"))
    }
    has, err := model.CheckFunctionHasTrigger(user, groupId, funcId)
    if err != nil {
        return nil, model.InvalidParameterError(model.FunctionDeleteError, i18n.GetMessage(request.Request, "fcs.trigger.detail"))
    }
    if has {
        return nil, model.InvalidParameterError(model.FunctionDeleteError, i18n.GetMessage(request.Request, "fcs.function.delete_trigger"))
    }
    return funcInfo, nil
}

func validateInvokeFunction(request *restful.Request) (*model.CallMessage, error) {
    groupId := request.PathParameter("groupId")
    functionId := request.PathParameter("functionId")
    versionName := request.PathParameter("versionName")
    event, err := ioutil.ReadAll(request.Request.Body)
    if err != nil {
        return nil, model.InvalidParameterError(model.FunctionRequestValidError, i18n.GetMessage(request.Request, "fcs.param_error"))
    }
    if string(event) != "" && !IsJSON(string(event)) {
        return nil, model.InvalidParameterError(model.FunctionRequestValidError, i18n.GetMessage(request.Request, "fcs.param_error"))
    }
    user := currentUser(request)
    funcInfo, err := model.GetFuncInfoByGroup(groupId, functionId, user)
    if err != nil {
        return nil, model.InvalidParameterError(model.FunctionRequestValidError, i18n.GetMessage(request.Request, "fcs.function.detail"))
    }
    _, err = model.GetVersion(functionId, versionName)
    if err != nil {
        return nil, model.InvalidParameterError(model.FunctionRequestValidError, i18n.GetMessage(request.Request, "fcs.version.version_info"))
    }
    invokeInfo := new(model.CallMessage)
    invokeInfo.GroupName = funcInfo.GroupName
    invokeInfo.FunctionName = funcInfo.FunctionName
    invokeInfo.VersionName = versionName
    invokeInfo.Event = string(event)
    return invokeInfo, nil
}

func validateIsSubnetUsed(request *restful.Request) (bool, error) {
    subnetId := request.PathParameter("subnetId")
    has, result, err := model.FindPortBindingBySubnet(subnetId)
    if err != nil {
        return false, model.InvalidParameterError(model.FunctionRequestValidError, i18n.GetMessage(request.Request, "fcs.vpc.query"))
    }
    if !has {
        return false, nil
    }
    if result.FunctionNum == 0 {
        return false, nil
    }
    return true, nil
}

func validateCreateAlias(request *restful.Request) (*model.FuncAlias, error) {
    groupId := request.PathParameter("groupId")
    funcId := request.PathParameter("functionId")
    alias := new(model.FuncAlias)
    if err := ReadAndValidateEntity(request, alias); err != nil {
        return nil, err
    }
    alias.AliasId = uuid.New().String()
    user := currentUser(request)
    funcInfo, err := model.GetFuncInfoByGroup(groupId, funcId, user)
    if err != nil {
        return nil, model.InvalidParameterError(model.FunctionGetError, i18n.GetMessage(request.Request, "fcs.function.detail"))
    }
    rest := 100
    for _, traffic := range alias.TrafficRule.Traffic {
        rest -= traffic.VersionWeight
    }
    if rest != 0 {
        return nil, model.InvalidParameterError(model.FunctionGetError, i18n.GetMessage(request.Request, "fcs.alias.sum_create"))
    }
    alias.Creator = funcInfo.Creator
    alias.AccountId = funcInfo.A5tId
    alias.GroupName = funcInfo.GroupName
    alias.FuncName = funcInfo.FunctionName
    alias.GroupId = funcInfo.GroupId
    alias.FuncId = funcInfo.Id
    return alias, nil
}

func validateUpdateAlias(request *restful.Request) (*model.FuncAlias, error) {
    groupId := request.PathParameter("groupId")
    funcId := request.PathParameter("functionId")
    user := currentUser(request)
    newAlias := new(model.FuncAlias)
    if err := ReadAndValidateEntity(request, newAlias); err != nil {
        return nil, err
    }
    funcInfo, err := model.GetFuncInfoByGroup(groupId, funcId, user)
    if err != nil {
        return nil, model.InvalidParameterError(model.FunctionAliasError, i18n.GetMessage(request.Request, "fcs.function.detail"))
    }
    oldAlias, err := model.GetAlias(funcId, newAlias.AliasName)
    if err != nil {
        return nil, model.InvalidParameterError(model.FunctionAliasError, i18n.GetMessage(request.Request, "fcs.alias.info"))
    }
    if !isNeedUpdateAlias(oldAlias, newAlias) {
        return oldAlias, model.NewBusinessError(errors.New("don't need update"), model.NoNeedUpdateResource)
    }
    rest := 100
    for _, traffic := range newAlias.TrafficRule.Traffic {
        rest -= traffic.VersionWeight
    }
    if rest != 0 {
        return nil, model.InvalidParameterError(model.FunctionGetError, i18n.GetMessage(request.Request, "fcs.alias.sum_update"))
    }
    oldAlias.Description = newAlias.Description
    oldAlias.TrafficRule = newAlias.TrafficRule
    oldAlias.Creator = funcInfo.Creator
    oldAlias.AccountId = funcInfo.A5tId
    oldAlias.GroupName = funcInfo.GroupName
    oldAlias.FuncName = funcInfo.FunctionName
    return oldAlias, nil
}

func validateDeleteAlias(request *restful.Request) (*model.FuncAlias, error) {
    groupId := request.PathParameter("groupId")
    funcId := request.PathParameter("functionId")
    aliasName := request.PathParameter("aliasName")
    user := currentUser(request)
    funcInfo, err := model.GetFuncInfoByGroup(groupId, funcId, user)
    if err != nil {
        return nil, model.InvalidParameterError(model.FunctionAliasError, i18n.GetMessage(request.Request, "fcs.function.detail"))
    }
    alias, err := model.GetAlias(funcId, aliasName)
    if err != nil {
        return nil, model.InvalidParameterError(model.FunctionAliasError, i18n.GetMessage(request.Request, "fcs.alias.info"))
    }
    alias.Creator = funcInfo.Creator
    alias.AccountId = funcInfo.A5tId
    alias.GroupName = funcInfo.GroupName
    alias.FuncName = funcInfo.FunctionName
    existTriggerList, err := model.GetUsingTargetTrigger(alias.FuncId, alias.AliasName, 2)
    if err != nil {
        return nil, model.InvalidParameterError(model.FunctionAliasError, i18n.GetMessage(request.Request, "fcs.trigger.detail"))
    }
    if len(existTriggerList) != 0 {
        var triggerNameList []string
        for _, trigger := range existTriggerList {
            triggerNameList = append(triggerNameList, trigger.Name)
        }
        return nil, model.InvalidParameterError(model.FunctionAliasError, i18n.GetMessage(request.Request, "fcs.alias.trigger", strings.Join(triggerNameList, "，")))
    }
    return alias, nil
}

func validateDeleteVersion(request *restful.Request) (*model.FuncInfo, error) {
    groupId := request.PathParameter("groupId")
    funcId := request.PathParameter("functionId")
    versionName := request.PathParameter("versionName")
    if versionName == model.FunctionLatestVersion {
        return nil, model.InvalidParameterError(model.FunctionAliasError, i18n.GetMessage(request.Request, "fcs.version.delete_latest"))
    }
    user := currentUser(request)
    funcInfo, err := model.GetFuncInfoByGroup(groupId, funcId, user)
    if err != nil {
        return nil, model.InvalidParameterError(model.FunctionAliasError, i18n.GetMessage(request.Request, "fcs.function.detail"))
    }
    version, err := model.GetVersion(funcId, versionName)
    if err != nil {
        return nil, model.InvalidParameterError(model.FunctionAliasError, i18n.GetMessage(request.Request, "fcs.version.version_info"))
    }
    funcInfo.VersionName = versionName
    funcInfo.EnableVpc = version.EnableVpc
    funcInfo.VpcInfo = version.VpcInfo
    existTriggerList, err := model.GetUsingTargetTrigger(version.FuncId, version.VersionName, 1)
    if err != nil {
        return nil, model.InvalidParameterError(model.FunctionAliasError, i18n.GetMessage(request.Request, "fcs.trigger.detail"))
    }
    if len(existTriggerList) != 0 {
        var triggerNameList []string
        for _, trigger := range existTriggerList {
            triggerNameList = append(triggerNameList, trigger.Name)
        }
        return nil, model.InvalidParameterError(model.FunctionAliasError, i18n.GetMessage(request.Request, "fcs.trigger.bind", strings.Join(triggerNameList, "，")))
    }
    aliasList, err := model.GetAliasList(funcId)
    if err != nil {
        return nil, model.InvalidParameterError(model.FunctionAliasError, i18n.GetMessage(request.Request, "fcs.alias.info"))
    }
    if len(aliasList) != 0 {
        var aliasNameList []string
        for _, alias := range aliasList {
            for _, traffic := range alias.TrafficRule.Traffic {
                if traffic.VersionName == versionName {
                    aliasNameList = append(aliasNameList, alias.AliasName)
                }
            }
        }
        if len(aliasNameList) != 0 {
            return nil, model.InvalidParameterError(model.FunctionAliasError, i18n.GetMessage(request.Request, "fcs.alias.bind", strings.Join(aliasNameList, "，")))
        }
    }

    return funcInfo, nil
}

func validateFunctionCallback(request *restful.Request) (*model.Invoke, error) {
    user := currentUser(request)
    if user.Name != model.Base.CallbackUser {
        return nil, errors.New("user has no right")
    }

    invoke := new(model.Invoke)
    if err := ReadAndValidateEntity(request, invoke); err != nil {
        return nil, err
    }
    // 防止返回数据过长
    if len(invoke.ReturnMsg) > 2048 {
        invoke.ReturnMsg = invoke.ReturnMsg[:2048]
    }
    return invoke, nil
}

func validateGetLog(request *restful.Request) (*model.LogQuery, error) {
    groupId := request.PathParameter("groupId")
    functionId := request.PathParameter("functionId")
    pageNoStr := request.QueryParameter("pageNo")
    pageSizeStr := request.QueryParameter("pageSize")
    startTimeStr := request.QueryParameter("startTime")
    endTimeStr := request.QueryParameter("endTime")
    requestId := request.QueryParameter("requestId")
    status := request.QueryParameter("status")
    if utils.IsSQLInject(groupId) || utils.IsSQLInject(functionId) || utils.IsSQLInject(requestId) || utils.IsSQLInject(status) {
        return nil, model.InvalidParameterError(model.FunctionLogError, i18n.GetMessage(request.Request, "fcs.param_error"))
    }
    pageNo, err := strconv.Atoi(pageNoStr)
    if err != nil {
        return nil, model.InvalidParameterError(model.FunctionLogError, i18n.GetMessage(request.Request, "fcs.param_error"))
    }
    pageSize, err := strconv.Atoi(pageSizeStr)
    if err != nil {
        return nil, model.InvalidParameterError(model.FunctionLogError, i18n.GetMessage(request.Request, "fcs.param_error"))
    }
    var startTime, endTime int64
    if startTimeStr != "" {
        startTime, err = strconv.ParseInt(startTimeStr, 10, 64)
        if err != nil {
            return nil, model.InvalidParameterError(model.FunctionMetricsError, i18n.GetMessage(request.Request, "fcs.param_error"))
        }
    }
    if endTimeStr != "" {
        endTime, err = strconv.ParseInt(endTimeStr, 10, 64)
        if err != nil {
            return nil, model.InvalidParameterError(model.FunctionMetricsError, i18n.GetMessage(request.Request, "fcs.param_error"))
        }
    }
    if status != "" && status != model.InvokeSuccess && status != model.InvokeFailure {
        return nil, model.InvalidParameterError(model.FunctionMetricsError, i18n.GetMessage(request.Request, "fcs.param_error"))
    }

    user := currentUser(request)
    funcInfo, err := model.GetAllFuncInfoByGroup(groupId, functionId, user)
    if err != nil {
        return nil, model.InvalidParameterError(model.FunctionLogError, i18n.GetMessage(request.Request, "fcs.function.detail"))
    }
    logQuery := new(model.LogQuery)
    logQuery.PageNo = pageNo
    logQuery.PageSize = pageSize
    logQuery.FuncId = funcInfo.Id
    logQuery.StartTime = startTime
    logQuery.EndTime = endTime
    logQuery.RequestId = requestId
    logQuery.Status = status
    return logQuery, nil
}

func validateGetMetrics(request *restful.Request) (*model.MetricQuery, error) {
    groupId := request.PathParameter("groupId")
    functionId := request.PathParameter("functionId")
    startTimeStr := request.QueryParameter("startTime")
    endTimeStr := request.QueryParameter("endTime")
    periodStr := request.QueryParameter("period")
    metricName := request.QueryParameter("metricName")
    // todo 本期暂无版本
    // version := request.QueryParameter("version")

    startTime, err := strconv.ParseInt(startTimeStr, 10, 64)
    if err != nil {
        return nil, model.InvalidParameterError(model.FunctionMetricsError, i18n.GetMessage(request.Request, "fcs.param_error"))
    }
    endTime, err := strconv.ParseInt(endTimeStr, 10, 64)
    if err != nil {
        return nil, model.InvalidParameterError(model.FunctionMetricsError, i18n.GetMessage(request.Request, "fcs.param_error"))
    }

    period, err := strconv.Atoi(periodStr)
    if err != nil {
        return nil, model.InvalidParameterError(model.FunctionMetricsError, i18n.GetMessage(request.Request, "fcs.param_error"))
    }
    if period == 0 {
        return nil, model.InvalidParameterError(model.FunctionMetricsError, i18n.GetMessage(request.Request, "fcs.param_error"))
    }

    metricMap := model.GetMetricMap()
    if ok, _ := metricMap[metricName]; !ok {
        return nil, model.InvalidParameterError(model.FunctionMetricsError, i18n.GetMessage(request.Request, "fcs.param_error"))
    }

    user := currentUser(request)
    funcInfo, err := model.GetAllFuncInfoByGroup(groupId, functionId, user)
    if err != nil {
        return nil, model.InvalidParameterError(model.FunctionLogError, i18n.GetMessage(request.Request, "fcs.function.detail"))
    }

    metricQuery := new(model.MetricQuery)
    metricQuery.FuncId = funcInfo.Id
    metricQuery.StartTime = startTime
    metricQuery.EndTime = endTime
    metricQuery.MetricName = metricName
    metricQuery.Period = period

    return metricQuery, nil
}

func ReadAndValidateGroup(request *restful.Request) (*model.Group, error) {
    user := currentUser(request)
    group := &model.Group{}
    groupId := request.PathParameter("groupId")
    if groupId != model.ExtIds {
        err := ReadAndValidateEntity(request, group)
        if err != nil {
            return nil, err
        }
        group.Id = uuid.New().String()
    } else {
        group.RegionCode = strings.Split(strings.Replace(model.Base.CallbackEndpoint, "http://", "", -1), ".")[0]
        if group.RegionCode == "cn-north3" {
            group.RegionName = "华北三"
        }
    }
    group.Id = uuid.New().String()
    group.Creator = user.Id
    group.CreatorName = user.Name
    group.A5tId = user.AccountId
    group.A5tName = user.AccountName
    return group, nil
}

func ReadAndValidateTrigger(request *restful.Request) (*model.Trigger, error) {
    user := currentUser(request)
    trigger := &model.Trigger{}
    err := ReadAndValidateEntity(request, trigger)
    if err != nil {
        return nil, err
    }
    trigger.Id = uuid.New().String()
    trigger.GroupId = request.PathParameter("groupId")
    trigger.FuncId = request.PathParameter("functionId")
    trigger.Creator = user.Id
    trigger.CreatorName = user.Name
    trigger.A5tId = user.AccountId
    trigger.A5tName = user.AccountName
    return trigger, nil
}

func ReadAndValidateDomain(request *restful.Request) (*model.Domain, error) {
    user := currentUser(request)
    domainInfo := &model.Domain{}
    err := ReadAndValidateEntity(request, domainInfo)
    if err != nil {
        return nil, err
    }
    domainInfo.Id = uuid.New().String()
    domainInfo.Creator = user.Id
    domainInfo.CreatorName = user.Name
    domainInfo.A5tId = user.AccountId
    domainInfo.A5tName = user.AccountName
    // 如果启用 HTTPS 需上传证书信息
    if strings.Contains(domainInfo.Protocol, model.ProtocolHTTPS) {
        if domainInfo.CertName == "" || domainInfo.CertCertificate == "" || domainInfo.CertKey == "" {
            err = fmt.Errorf("HTTP,HTTPS protocal lack of certName, certCertificate, certKey")
            return nil, err
        }
        // 校验证书和私钥的 PEM 格式
        blockCert, _ := pem.Decode([]byte(domainInfo.CertCertificate))
        if blockCert == nil {
            err = fmt.Errorf("certCertificate is not in PEM format")
            return nil, err
        }
        blockKey, _ := pem.Decode([]byte(domainInfo.CertKey))
        if blockKey == nil {
            err = fmt.Errorf("certKey is not in PEM format")
            return nil, err
        }
        domainInfo.Cert = &model.Cert{
            Id:          uuid.New().String(),
            Creator:     domainInfo.Creator,
            CreatorName: domainInfo.CreatorName,
            A5tId:       domainInfo.A5tId,
            A5tName:     domainInfo.A5tName,
            DomainId:    domainInfo.Id,
            Name:        domainInfo.CertName,
            Certificate: domainInfo.CertCertificate,
            Key:         domainInfo.CertKey,
        }
    }

    // 校验路由规则，path 不允许相同
    ingressRuleValue, err := business.GetIngressRuleValue(domainInfo)
    if err != nil {
        _ = logger.Error("ReadAndValidateDomain GetIngressRuleValue error %s", err.Error())
        return nil, err
    }
    if ingressRuleValue.HTTP != nil && len(ingressRuleValue.HTTP.Paths) > 1 {
        pathMap := map[string]int{}
        for _, path := range ingressRuleValue.HTTP.Paths {
            if _, exist := pathMap[path.Path]; exist {
                err = fmt.Errorf("duplicated routeConfig path %s", path.Path)
                return nil, err
            }
            pathMap[path.Path] = 1
        }
    }

    return domainInfo, nil
}

func ReadAndValidateEntity(request *restful.Request, entityPointer interface{}) error {
    return validator.ReadAndValidateEntity(request, entityPointer)
}

func IsJSON(str string) bool {
    var js json.RawMessage
    return json.Unmarshal([]byte(str), &js) == nil
}

func isNeedUpdateAlias(old *model.FuncAlias, new *model.FuncAlias) bool {
    if old.Description == new.Description && reflect.DeepEqual(old.TrafficRule, new.TrafficRule) {
        return false
    }
    return true
}
func checkGroupId(api *APIHandler, request *restful.Request, response *restful.Response, functionInfo *model.FuncInfo) {
    user := currentUser(request)
    exist, err := model.CheckGroupExist(user.Id, functionInfo.GroupName)
    if err != nil {
        _ = logger.Error("handleCheckGroupExist CheckGroupExist error %v", err)
    }
    if !exist {
        fcClient, err := GetFcClient(api)
        if err != nil {
            _ = logger.Error("handleCreateFunction get cmanagerconfig error %v", err)
            return
        }
        group, err := ReadAndValidateGroup(request)
        if err != nil {
            _ = logger.Error("handleCreateGroup ReadAndValidateGroup error %v", err)
            return
        }
        group.Id = functionInfo.GroupName
        group.Name = functionInfo.GroupName + rand.String(7)
        ns := ksvc.GetGroupNs(group)
        if err := namespace.CreateNamespace(fcClient, ns); err != nil {
            _ = logger.Error("handleCreateGroup CreateNamespace err : %v", err)
            if strings.Contains(err.Error(), "already exists") {
                handleInternalError(response, model.InternalServerError(model.TriggerError, i18n.GetMessage(request.Request, "fcs.group.name_dup")))
                return
            } else {
                handleInternalError(response, model.InternalServerError(model.TriggerError, i18n.GetMessage(request.Request, "fcs.group.create")))
                return
            }
        }
        group.DisplayName = functionInfo.GroupName
        group, err = model.CreateGroup(group)
        if err != nil {
            _ = namespace.DeleteNamespace(fcClient, ns)
            return
        }
        //// 添加pod隔离，异步执行
        //go func() {
        //	_ = networkpolicy.CreateDefaultPolicy(fcClient, ns)
        //}()
        //// 添加pod访问外网策略
        //go func() {
        //	_ = networkpolicy.CreateFunctionPublicPolicy(fcClient, ns)
        //}()
    }

}

func uploadCode(functionInfo *model.FuncInfo, request *restful.Request, response *restful.Response) (*model.FuncInfoExt, error) {
    if functionInfo.CodeInfo.ZipFile != "" {
        decodeBytes, err := base64.StdEncoding.DecodeString(functionInfo.CodeInfo.ZipFile)
        if err != nil {
            handleInternalError(response, model.InternalServerError(model.TriggerError, i18n.GetMessage(request.Request, "fcs.code.upload")))
        }
        handleUploadCodeEx(decodeBytes, functionInfo, request, response)
    } else if functionInfo.CodeInfo.CodeFile != "" {
        decodeBytes, err := downloadExtCodeEx(functionInfo)
        if err != nil {
            return nil, err
        }
        handleUploadCodeEx(decodeBytes, functionInfo, request, response)
    }

    functionInfoExt := new(model.FuncInfoExt)
    functionInfoExt.FuncId = functionInfo.Id
    functionInfoExt.GroupId = functionInfo.GroupId
    functionInfoExt.GroupName = functionInfo.GroupName
    functionInfoExt.FunctionName = functionInfo.FunctionName
    functionInfoExt.Status = "success"
    //functionInfoExt.FunctionAddress = "/ifcs/v1/groups/" + functionInfo.GroupId + "/functions/" + functionInfo.Id + "/versions/" + functionInfo.VersionName + "/invoke"
    user := currentUser(request)
    url := fmt.Sprintf("%s/sync/%s/%s/%s.%s", model.Base.FunctionInvokeHost, user.Id, functionInfo.GroupName, functionInfo.FunctionName, model.FunctionLatestVersion)
    functionInfoExt.FunctionAddress = url
    return functionInfoExt, nil
}

func downloadExtCodeEx(functionInfo *model.FuncInfo) ([]byte, error) {
    extCode := functionInfo.CodeInfo
    extCodeUri := extCode.CodeUri
    rname := rand.String(7)
    root := fmt.Sprintf("/temp/%s/code/", rname)
    zipRoot := fmt.Sprintf("/temp/%s/", rname)

    cmd = exec.Command("/bin/bash", "-c", " mkdir -p "+root)
    _, err := cmd.CombinedOutput()
    if err != nil {
        fmt.Println(err)
    }

    for i := 0; i < len(extCodeUri); i++ {
        exturl := extCodeUri[i]
        filename := strings.Split(exturl, "=")[len(strings.Split(exturl, "="))-1]

        r, err := http.NewRequest("GET", exturl, nil)
        if err != nil {
            panic(err)
        }
        resp, err := http.DefaultClient.Do(r)
        if err != nil {
            panic(err)
        }
        defer func() {
            _ = resp.Body.Close()
        }()

        cmd = exec.Command("/bin/bash", "-c", " touch "+root+filename)
        _, err = cmd.CombinedOutput()
        if err != nil {
            fmt.Println(err)
        }

        data, err := ioutil.ReadAll(resp.Body)
        if err != nil {
            panic(err)
        }
        _ = ioutil.WriteFile(root+filename, data, 0600)

    }

    err = Zip(root, zipRoot+"code.zip")
    if err != nil {
        return nil, err
    }

    f, err := ioutil.ReadFile(filepath.Clean(zipRoot + "code.zip"))
    if err != nil {
        fmt.Println("read fail", err)
    }

    cmd = exec.Command("/bin/bash", "-c", " rm -rf  "+zipRoot)
    _, err = cmd.CombinedOutput()
    if err != nil {
        fmt.Println(err)
    }

    return f, nil
}

func Zip(srcDir string, zipFileName string) error {

    dir, err := ioutil.ReadDir(srcDir)
    if err != nil {
        return err
    }
    if len(dir) == 0 {
        return nil
    }
    _ = os.RemoveAll(zipFileName)

    zipfile, _ := os.Create(zipFileName)
    defer func() {
        _ = zipfile.Close()
    }()

    archive := zip.NewWriter(zipfile)
    defer func() {
        _ = archive.Close()
    }()

    _ = filepath.Walk(srcDir, func(path string, info os.FileInfo, _ error) error {

        if path == srcDir {
            return nil
        }

        header, _ := zip.FileInfoHeader(info)

        header.Name = strings.TrimPrefix(path, srcDir)

        if info.IsDir() {
            header.Name += `/`
        } else {
            header.Method = zip.Deflate
        }

        writer, _ := archive.CreateHeader(header)
        if !info.IsDir() {
            file, _ := os.Open(filepath.Clean(path))
            defer func() {
                _ = file.Close()
            }()
            _, _ = io.Copy(writer, file)
        }

        return nil
    })
    return nil
}

func handleUploadCodeEx(decodeBytes []byte, code *model.FuncInfo, request *restful.Request, response *restful.Response) {
    user := currentUser(request)
    bucketName := model.Base.OssBucket
    date := time.Now().UTC().Format(http.TimeFormat)
    codeObjectName := user.AccountId + "/" + code.GroupName + "/" + code.FunctionName + "/" + code.VersionName + "/" + "code.zip"
    checkObjectName := user.AccountId + "/" + code.GroupName + "/" + code.FunctionName + "/" + code.VersionName + "/" + "checksum"
    codeSignedStr := utils.GetSignedStr(bucketName, codeObjectName, "", date, Ak, Sk, map[string]string{})
    checkSignedStr := utils.GetSignedStr(bucketName, checkObjectName, "", date, Ak, Sk, map[string]string{})
    codeUrl := model.Base.OssEndpoint + "/" + bucketName + "/" + codeObjectName
    checkUrl := model.Base.OssEndpoint + "/" + bucketName + "/" + checkObjectName

    if len(decodeBytes) > 0 {
        err := business.OssUploadOrCopy(codeUrl, date, codeSignedStr, strings.NewReader(string(decodeBytes)), map[string]string{})
        if err != nil {
            handleInternalError(response, model.InternalServerError(model.TriggerError, i18n.GetMessage(request.Request, "fcs.code.upload")))
            return
        }
        //校验和上传到公共库
        checkSum, err := business.GetCheckSum(strings.NewReader(string(decodeBytes)))
        if err != nil {
            handleInternalError(response, model.InternalServerError(model.TriggerError, i18n.GetMessage(request.Request, "fcs.code.upload")))
            return
        }
        err = business.OssUploadOrCopy(checkUrl, date, checkSignedStr, strings.NewReader(checkSum), map[string]string{})
        if err != nil {
            handleInternalError(response, model.InternalServerError(model.TriggerError, i18n.GetMessage(request.Request, "fcs.code.upload")))
            return

        }
    }

}

func findFuncInfoFromDB(groupId, funcId string, user *resource.User) (*model.FuncInfo, *model.FuncVersion, error) {
    funcInfo, err := model.GetFuncInfoByGroup(groupId, funcId, user)
    if err != nil {
        return nil, nil, model.InvalidParameterError(model.FunctionUpdateError, "查询函数信息失败")
    }
    latestVersionInfo, err := model.GetLatestVersion(funcInfo.Id)
    if err != nil {
        return nil, nil, model.InvalidParameterError(model.FunctionUpdateError, "查询版本信息失败")
    }
    funcInfo.Description = latestVersionInfo.Description
    funcInfo.Timeout = latestVersionInfo.Timeout
    funcInfo.Envs = latestVersionInfo.Envs
    funcInfo.MemorySize = latestVersionInfo.MemorySize
    funcInfo.GpuNum = latestVersionInfo.GpuNum
    funcInfo.GpuType = latestVersionInfo.GpuType
    funcInfo.VersionId = latestVersionInfo.VersionId
    funcInfo.VersionName = latestVersionInfo.VersionName
    funcInfo.Handler = latestVersionInfo.Handler
    funcInfo.Concurrency = latestVersionInfo.Concurrency
    funcInfo.ReserveNumber = latestVersionInfo.ReserveNumber
    funcInfo.EnablePublic = latestVersionInfo.EnablePublic
    funcInfo.EnableVpc = latestVersionInfo.EnableVpc
    funcInfo.VpcInfo = latestVersionInfo.VpcInfo
    funcInfo.ContainerImage = latestVersionInfo.ContainerImage
    funcInfo.ContainerCommand = latestVersionInfo.ContainerCommand
    funcInfo.ContainerArgs = latestVersionInfo.ContainerArgs
    funcInfo.ContainerPort = latestVersionInfo.ContainerPort
    funcInfo.InitHandler = latestVersionInfo.InitHandler
    funcInfo.InitTimeout = latestVersionInfo.InitTimeout
    funcInfo.BindingLayers = latestVersionInfo.BindingLayers
    return funcInfo, latestVersionInfo, nil
}
